/*
 * This file is part of VCompiler.
 * 
 * VCompiler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * VCompiler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with VCompiler.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Vector;

/*
 * TODO:	1) Add support for functions.
 * 
 * WARNING:	This is a mistake that is easy to forget. There are a lot of functions that produce assembly code.
 * 			These functions have a "save" as an input parameter. Don't forget to catch the returned version of
 * 			that "save" or else it will not be written to the assembly file.
 * 				e.i : save = Add(save);
 */

public class VCompiler
{
	public char Look;
	public StringBuffer LookBuffer = new StringBuffer(""); //This is an undo mechanism.
	public BufferedReader br;
	public BufferedReader PeekBr;
	public BufferedWriter out;
	public String header = "";
	public String data = "";
	public String code = "";
	public String footer = "";
	public Vector<String[]> allVariables = new Vector<String[]>();
	
	public int byteCounter = 0;
	
	public int Line = 1;
	public int LCount = 0; //Label Count, this is always incremented.
	public int DCount = 0; //Division Label.
	//A label looks like this: L1 , L2 , L3 , etc
	//Note: A Label cannot start with a number.
	
	public String[][] conditions =
		{	//Note: The order in which they are checked is important.
			{
				"==",	"<",	">",	"!=",	"<=",	">=",	"!<",	"!>"
			},
			{
				"jne",	"jna",	"jnb",	"je",	"jb",	"ja",	"ja",	"jb"
			},
			{
				"je",	"ja",	"jb",	"jne",	"jnb",	"jna",	"jna",	"jnb"
			}
		};
	
	public String[] ArithOperator =
		{
			"=", "+=", "-=", "*=", "/=", "%=", "=+", "=-", "=*", "=/", "=%"
		};
	
	public String lastOperator = "";
	
	/**
	 * This is the constructor. It is used to insert the proper default lines of code in the asm file. It can be considered as a template. In the future, there will be more template choices.
	 * @throws IOException
	 */
	public VCompiler() throws IOException
	{
		br = new BufferedReader(new FileReader("Input.vyne"));
		out = new BufferedWriter(new FileWriter("Output.asm"));
		header = "format PE console\n" +
				"entry start\n" +
				"include 'C:\\fasm\\include\\win32ax.inc'\n" +
				"section '.idata' import data readable\n" +
				"library kernel,'kernel32.dll', msvcrt,'msvcrt.dll', User32,'User32.dll'\n" +
				"import kernel, ExitProcess,'ExitProcess',Sleep,'Sleep'\n" +
				"import msvcrt, printf,'printf', getchar,'_fgetchar'\n" +
				"import User32, wsprintf,'wsprintfA'\n";
		data =	"section '.data' data readable writeable\n" +
				"\tdivisionZero db 'Division by zero error!', 13, 10 , 0\n" +
				"\tstrInteger db '%d', 13, 10 , 0\n" +
				"\tstrBuffer rb 64\n";
		
		code =	"section '.code' code readable executable\n" +
				"start:\n" +
				"\tpush ebp\n" +
				"\tmov ebp, esp\n";
		
		footer = "\tstdcall [ExitProcess],0";
		GetChar();
		SkipWhite();
	}
	
	/**
	 * EmitFree is used to write some lines of code to the asm file without anything added to those lines.
	 * @param section The string that the variable s will be concatenated to. This string holds some previous lines of code.
	 * @param s This is the string that will be added to the variable section.
	 * @return The function will return the new version of the variable section.
	 * @throws IOException
	 */
	public String EmitFree(String section, String s) throws IOException
	{
		return (section += s);
	}
	
	/**
	 * Emit is used when writing a single line of code to the asm file. It adds the proper indentation, but no new line is added. It is usually better to use the function EmitLn
	 * @param section The string that the variable s will be concatenated to. This string holds some previous lines of code.
	 * @param s This is the string that will be added to the variable section.
	 * @return The function will return the new version of the variable section.
	 * @throws IOException
	 */
	public String Emit(String section, String s) throws IOException
	{
		//out.write("\t" + s);
		return (section += "\t" + s);
	}
	
	/**
	 * EmitLn is the regular function to use when writing a single line of code to the asm file. It adds both the proper indentation, and a new line.
	 * @param section The string that the variable s will be concatenated to. This string holds some previous lines of code.
	 * @param s This is the string that will be added to the variable section.
	 * @return The function will return the new version of the variable section.
	 * @throws IOException
	 */
	public String EmitLn(String section, String s) throws IOException
	{
		section = Emit(section, s);
		//out.newLine();
		return (section += "\n");
	}
	
	/**
	 * PeekLabel is used to be able to know which label is going to be used after a certain amount of NewLabel call without changing anything to the label count.
	 * @param Amount The amount of times that NewLabel will be call.
	 * @return The label that will be used after a certain amount of NewLabel call.
	 */
	public String PeekLabel(int Amount)
	{
		String Label = "L" + (LCount + Amount);
		return Label;
	}
	
	/**
	 * NewLabel is a function that returns an unused label that can be used in the asm file.
	 * @return An unused label.
	 */
	public String NewLabel()
	{
		String Label = PeekLabel(0);
		LCount++;
		return Label;
	}
	
	/**
	 * PeekDivLabel is similar to PeekLabel. It is used to be able to know which label is going to be used after a certain amount of NewDivLabel call without changing anything to the label count.
	 * @param Amount The amount of times that NewDivLabel will be call.
	 * @return The label that will be used after a certain amount of NewDivLabel call.
	 */
	public String PeekDivLabel(int Amount)
	{
		String Label = "D" + (DCount + Amount);
		return Label;
	}
	
	/**
	 * NewDivLabel is similar to NewLabel. It is a function that returns an unused label that can be used in the asm file.
	 * @return An unused label.
	 */
	public String NewDivLabel()
	{
		String Label = PeekDivLabel(0);
		DCount++;
		return Label;
	}
	
	/**
	 * WrapVariables is a function used mainly for debugging. It prints the name of a variable with its value.
	 * @param save The string that the generated lines of code will be concatenated to.
	 * @param name The name of the variable.
	 * @param position The offset of the variable from the ebp register.
	 * @return Returns the new version of the variable save.
	 * @throws IOException
	 */
	public String WrapVariables(String save, String name, int position) throws IOException
	{
		//TODO: This will have to be scaled for different variable types. (Now that the loop is gone this is fixed.)
		data = EmitLn(data, name + "_msg db " + "'" + name + ": ', 0");
		
		save = EmitLn(save, "mov ecx, ebp");
		if(position > 0)
		{
			save = EmitLn(save, "sub ecx, " + position);
		}
		save = EmitLn(save, "mov esi, [ecx]\n" +
				"\tccall [wsprintf], strBuffer, strInteger, esi\n" +
				"\tccall [printf], "+ name + "_msg\n" +
				"\tccall [printf], strBuffer");
		//data = EmitLn(data, "int_var db " + allVariables.size() + " dup(0)");
		
		return save;
	}
	
	/**
	 * Wrap file is the function that writes everything the the asm file.
	 * @throws IOException
	 */
	public void WrapFile() throws IOException
	{
		//WrapVariables();
		out.write(header);
		out.write(data);
		out.write(code);
		out.write(footer);
	}
	
	/**
	 * GetChar is used to read the next character from the source code and puts in into the variable look.
	 */
	public void GetChar()
	{
		try
		{
			//BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1);
			if(LookBuffer.length() > 0)
			{ //Empty LookBuffer before reading more from the file.
				Look = LookBuffer.charAt(LookBuffer.length() - 1);
				LookBuffer.deleteCharAt(LookBuffer.length() - 1);
				
				//System.out.println("Look = " + Look);
			}
			else
			{
				Look = (char) br.read();
			}
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * ReplaceChar is used to place a character into Look.
	 * @param c The character that will be added.
	 */
	public void ReplaceChar(char c)
	{
		//System.out.println("Replacing! " + c + "\n\t" + LookBuffer.toString());
		//LookBuffer.insert(LookBuffer.length(), c); //I believe calling this function and then calling GetChar ends up cancelling itself.
		//System.out.println("Done!\n\t" + LookBuffer.toString());
		//GetChar();
		
		Look = c; //This should end up being the same as the lines above.
	}
	
	/**
	 * ReplaceChar is used to add a character to the source code that will later be picked up by GetChar, and a character in Look.
	 * @param c The character that will be added to the source code.
	 * @param restore The character that will go in the variable Look.
	 */
	public void ReplaceChar(char c, char restore)
	{
		//System.out.println("Replacing! " + c + "\n\t" + LookBuffer.toString());
		LookBuffer.insert(LookBuffer.length(), c);
		//System.out.println("Done!\n\t" + LookBuffer.toString());
		Look = restore;
	}
	
	/**
	 * ReplaceString is used to add a whole string to the source code. That string will later be picked up by GetChar.
	 * @param s The string that will be added to the source code.
	 */
	public void ReplaceString(String s)
	{
		for(int i = s.length() - 1; i >= 0; i--)
		{
			LookBuffer.insert(LookBuffer.length(), s.charAt(i));
		}
		//LookBuffer.insert(LookBuffer.length(), s);
		//System.out.println("Done!\n\t" + LookBuffer.toString());
		
		GetChar();
	}
	
	/**
	 * ReplaceString is used to add a whole string to the source code. That string will later be picked up by GetChar. It also places a character in Look.
	 * @param s The string that will be added to the source code.
	 * @param restore The character that will go in the variable Look.
	 */
	public void ReplaceString(String s, char restore)
	{
		for(int i = s.length() - 1; i >= 0; i--)
		{
			LookBuffer.insert(LookBuffer.length(), s.charAt(i));
		}
		//LookBuffer.insert(LookBuffer.length(), s);

		Look = restore;
	}
	
	public void Error(String s)
	{
		Toolkit.getDefaultToolkit().beep();
		System.out.println("\nError: " + s + " on line " + Line);
	}
	
	public void Abort(String s)
	{
		Error(s);
		System.exit(1);
	}
	
	public void Expected(String s)
	{
		Abort("Expected « " + s + " »");
	}
	
	public void Expected(char c)
	{
		Expected(c + "");
	}
	
	public boolean IsAlpha(char c)
	{
		if(Character.isLetter(c))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public boolean IsDigit(char c)
	{
		if(Character.isDigit(c))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public boolean IsAlNum(char c)
	{
		if(IsAlpha(c) || IsDigit(c))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public boolean IsAddop(char c)
	{
		if((Look == '+') || (Look == '-'))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public boolean IsArith() throws IOException
	{
		String Symbol = GetSymbol();
		for(int i = 0; i < ArithOperator.length; i++)
		{
			if(ArithOperator[i].equals(Symbol))
			{
				ReplaceString(Symbol + " " + Look);
				return true;
			}
		}
		ReplaceString(Symbol + Look);
		return false;
	}
	
	public boolean IsArith(String s)
	{
		for(int i = 0; i < ArithOperator.length; i++)
		{
			if(ArithOperator[i].equals(s))
			{
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * This will return 0 if there is no comment.
	 * It will return 1 if the comment is a single line comment.
	 * It will return 2 if the comment is a multiple line comment.
	 */
	public int IsStartComment()
	{
		if(Look == '/')
		{
			GetChar();
			if(Look == '/')
			{
				GetChar();
				return 1;
			}
			else if(Look == '*')
			{
				GetChar();
				return 2;
			}
			else
			{
				ReplaceChar(Look, '/');
			}
		}
		return 0;
	}
	
	public boolean IsEndMultiComment()
	{
		if(Look == '*')
		{
			GetChar();
			if(Look == '/')
			{
				GetChar();
				return true;
			}
			else
			{
				ReplaceChar(Look, '*');
			}
		}
		return false;
	}
	
	public void comment() throws IOException
	{
		int StartLine = Line;
		
		while(StartLine == Line)
		{
			GetChar();
			SkipWhite();
		}
	}
	
	public boolean multiComment() throws IOException
	{
		while(!IsEndMultiComment())
		{
			if(IsStartComment() == 2)
			{
				if(multiComment())
				{
					System.out.println("Stop the comment!");
					return true;
				}
			}
			else
			{
				//System.out.println(Look + " " + (int) Look);
				GetChar();
				
				if(((int) Look) == 65535)
				{
					Expected("Comment Terminator");
				}
			}
			SkipWhite();
		}
		
		if(Look == '/')
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public boolean IsWhite(char c) throws IOException
	{
		int commentNumber = IsStartComment();
		if(commentNumber == 1)
		{
			comment();
			return true;
		}
		else if(commentNumber == 2)
		{
			multiComment();
			return true;
		}
		else if((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))
		{
			if(c == '\n')
			{
				Line++;
			}
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public void SkipWhite() throws IOException
	{
		while(IsWhite(Look))
		{
			GetChar();
		}
	}
	
	public void Match(char x) throws IOException
	{
		if(Look == x)
		{
			GetChar();
			SkipWhite();
		}
		else
		{
			Expected(x);
		}
	}
	
	public String GetName() throws IOException
	{
		String Token = "";
		if(!IsAlpha(Look))
		{
			System.out.println(Look);
			Expected("Name");
		}
		while(IsAlNum(Look))
		{
			Token += Look;
			GetChar();
		}
		SkipWhite();
		return Token;
	}
	
	public String GetNum() throws IOException
	{
		String Value = "";
		if(!IsDigit(Look))
		{
			Expected("Integer");
		}
		while(IsDigit(Look))
		{
			Value += Look;
			GetChar();
		}
		SkipWhite();
		return Value;
	}
	
	public String GetSymbol() throws IOException
	{
		String Token = "";
		while((!IsWhite(Look)) && (Look != '(') && (Look != ')') && !IsAlNum(Look) && ((int) Look) != 65535)
		{
			Token += Look;
			GetChar();
		}
		SkipWhite();
		return Token;
	}
	
	public String GetArith() throws IOException
	{
		String Symbol = GetSymbol();
		
		if(!IsArith(Symbol))
		{
			Expected("Arithmetic Operator " + Symbol);
		}
		
		return Symbol;
	}
	
	public int GetOperator() throws IOException
	{
		boolean ValidOperator = false;
		String Check = GetSymbol();
		int position = 0;
		for(int i = 0; i < conditions[0].length; i++)
		{
			//System.out.println("Match " + i + " " + Check);
			if(Check.equals(conditions[0][i]))
			{
				//System.out.println(Check + " " + conditions[0][i] + " " + conditions[1][i]);
				position = i;
				
				i = conditions[0].length;
				ValidOperator = true;
			}
		}
		if(!ValidOperator)
		{
			Expected("boolean operator");
		}
		return position;
	}
	
	public String Add(String save) throws IOException
	{
		Match('+');
		save = Term(save);
		save = EmitLn(save, "pop ebx");
		save = EmitLn(save, "add eax, ebx");
		
		return save;
	}
	
	public String Substract(String save) throws IOException
	{
		Match('-');
		save = Term(save);
		save = EmitLn(save, "mov ebx, eax");
		save = EmitLn(save, "pop eax");
		save = EmitLn(save, "sub eax, ebx");
		
		return save;
	}
	
	public String Zero(String save) throws IOException
	{
		/*
		 * TODO: Perhaps the division by zero test will have to be redone.
		 */
		
		save = EmitLn(save, "cmp eax, 0");
		save = EmitLn(save, "jne " + PeekDivLabel(0));
		save = EmitLn(save, "ccall [printf], divisionZero");
		save = EmitLn(save, "stdcall [ExitProcess],0");
		save = EmitFree(save, NewDivLabel() + ":");
		
		return save;
	}
	
	public String Multiply(String save) throws IOException
	{
		Match('*');
		save = Factor(save);
		save = EmitLn(save, "pop ebx");
		save = EmitLn(save, "mul ebx");
		
		return save;
	}
	
	public String Divide(String save) throws IOException
	{
		Match('/');
		save = Factor(save);
		save = Zero(save);
		save = EmitLn(save, "mov ebx, eax");
		save = EmitLn(save, "pop eax");
		save = EmitLn(save, "cdq");
		save = EmitLn(save, "idiv ebx");
		
		return save;
	}
	
	public String Modulus(String save) throws IOException
	{
		Match('%');
		save = Factor(save);
		save = Zero(save);
		save = EmitLn(save, "mov ebx, eax");
		save = EmitLn(save, "pop eax");
		save = EmitLn(save, "cdq");
		save = EmitLn(save, "idiv ebx");
		save = EmitLn(save, "mov eax, edx");
		
		return save;
	}
	
	public String Arith(String save, int i) throws IOException
	{
		save = Expression(save);
		save = EmitLn(save, "mov ecx, ebp");
		if(!allVariables.elementAt(i)[2].equals("0"))
		{
			save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
		}
		save = EmitLn(save, "mov [ecx], eax");
		
		return save;
	}
	public String ArithA(String save, int i) throws IOException
	{
		return Arith(save, i);
	}
	
	public String ArithPA(String save, String Name, int i) throws IOException
	{
		ReplaceString(Name + "+" + Look);
		
		return Arith(save, i);
	}
	public String ArithSA(String save, String Name, int i) throws IOException
	{
		ReplaceString(Name + "-" + Look);
		
		return Arith(save, i);
	}
	public String ArithTA(String save, String Name, int i) throws IOException
	{
		ReplaceString(Name + "*" + Look);
		
		return Arith(save, i);
	}
	public String ArithDA(String save, String Name, int i) throws IOException
	{
		ReplaceString(Name + "/" + Look);
		
		return Arith(save, i);
	}
	public String ArithMA(String save, String Name, int i) throws IOException
	{
		ReplaceString(Name + "%" + Look);
		
		return Arith(save, i);
	}
	public String ArithAP(String save, String Name, int i) throws IOException
	{		
		save = Expression(save);
		save = EmitLn(save, "push eax");
		
		ReplaceString("+" + Name + Look);
		
		save = Add(save);
		
		save = EmitLn(save, "mov ecx, ebp");
		if(!allVariables.elementAt(i)[2].equals("0"))
		{
			save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
		}
		save = EmitLn(save, "mov [ecx], eax");
		
		return save;
	}
	public String ArithAS(String save, String Name, int i) throws IOException
	{		
		save = Expression(save);
		save = EmitLn(save, "push eax");
		
		ReplaceString("-" + Name + Look);
		
		save = Substract(save);
		
		save = EmitLn(save, "mov ecx, ebp");
		if(!allVariables.elementAt(i)[2].equals("0"))
		{
			save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
		}
		save = EmitLn(save, "mov [ecx], eax");
		
		return save;
	}
	public String ArithAT(String save, String Name, int i) throws IOException
	{		
		save = Expression(save);
		save = EmitLn(save, "push eax");
		
		ReplaceString("*" + Name + Look);
		
		save = Multiply(save);
		
		save = EmitLn(save, "mov ecx, ebp");
		if(!allVariables.elementAt(i)[2].equals("0"))
		{
			save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
		}
		save = EmitLn(save, "mov [ecx], eax");
		
		return save;
	}
	public String ArithAD(String save, String Name, int i) throws IOException
	{		
		save = Expression(save);
		save = EmitLn(save, "push eax");
		
		ReplaceString("/" + Name + Look);
		
		save = Divide(save);
		
		save = EmitLn(save, "mov ecx, ebp");
		if(!allVariables.elementAt(i)[2].equals("0"))
		{
			save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
		}
		save = EmitLn(save, "mov [ecx], eax");
		
		return save;
	}
	public String ArithAM(String save, String Name, int i) throws IOException
	{		
		save = Expression(save);
		save = EmitLn(save, "push eax");
		
		ReplaceString("%" + Name + Look);
		
		save = Modulus(save);
		
		save = EmitLn(save, "mov ecx, ebp");
		if(!allVariables.elementAt(i)[2].equals("0"))
		{
			save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
		}
		save = EmitLn(save, "mov [ecx], eax");
		
		return save;
	}
	
	public String Factor(String save) throws IOException
	{
		if(Look == '(')
		{
			Match('(');
			save = Expression(save);
			Match(')');
		}
		else if(IsAlpha(Look))
		{
			boolean Init = false;
			String Name = GetName();
			for(int i = 0; i < allVariables.size(); i++)
			{
				if(Name.equals(allVariables.elementAt(i)[0]))
				{
					Init = true;
					save = EmitLn(save, "mov ecx, ebp");
					if(!allVariables.elementAt(i)[2].equals("0"))
					{
						save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
					}
					save = EmitLn(save, "mov eax, [ecx]");
				}
			}
			if(!Init)
			{
				Abort("Undeclared variable « " + Name + " »");
			}
		}
		else
		{
			save = EmitLn(save, "mov eax, " + GetNum());
		}
		
		return save;
	}
	
	public String Term(String save) throws IOException
	{
		save = Factor(save);
		while((Look == '*') || (Look == '/') || (Look == '%'))
		{
			save = EmitLn(save, "push eax");
			
			if(Look == '*')
			{
				save = Multiply(save);
			}
			else if(Look == '/')
			{
				save = Divide(save);
			}
			else if(Look == '%')
			{
				save = Modulus(save);
			}
			else
			{
				Expected("Mulop");
			}
		}
		return save;
	}
	
	public String Expression(String save) throws IOException
	{
		if(IsAddop(Look))
		{
			save = EmitLn(save, "xor eax, eax");
		}
		else
		{
			save = Term(save);
		}
		while((Look == '+') || (Look == '-'))
		{
			save = EmitLn(save, "push eax");
			
			if(Look == '+')
			{
				save = Add(save);
			}
			else if(Look == '-')
			{
				save = Substract(save);
			}
			else
			{
				Expected("Addop");
			}
		}
		
		return save;
	}
	
	public String Assignment(String save, String Name) throws IOException
	{
		boolean Init = false;
		//Match('=');
		
		String AOperator = GetArith();
		
		for(int i = 0; i < allVariables.size(); i++)
		{
			if(Name.equals(allVariables.elementAt(i)[0]))
			{
				Init = true;
				
				if(AOperator.equals("="))
				{
					save = ArithA(save, i);
				}
				else if(AOperator.equals("+="))
				{
					save = ArithPA(save, Name, i);
				}
				else if(AOperator.equals("-="))
				{
					save = ArithSA(save, Name, i);
				}
				else if(AOperator.equals("*="))
				{
					save = ArithTA(save, Name, i);
				}
				else if(AOperator.equals("/="))
				{
					save = ArithDA(save, Name, i);
				}
				else if(AOperator.equals("%="))
				{
					save = ArithMA(save, Name, i);
				}
				else if(AOperator.equals("=+"))
				{
					save = ArithAP(save, Name, i);
				}
				else if(AOperator.equals("=-"))
				{
					save = ArithAS(save, Name, i);
				}
				else if(AOperator.equals("=*"))
				{
					save = ArithAT(save, Name, i);
				}
				else if(AOperator.equals("=/"))
				{
					save = ArithAD(save, Name, i);
				}
				else if(AOperator.equals("=%"))
				{
					save = ArithAD(save, Name, i);
				}
				/*save = Expression(save);
				save = EmitLn(save, "mov ecx, ebp");
				if(!allVariables.elementAt(i)[2].equals("0"))
				{
					save = EmitLn(save, "sub ecx, " + allVariables.elementAt(i)[2]);
				}
				save = EmitLn(save, "mov [ecx], eax");*/
			}
		}
		if(!Init)
		{
			Abort("Undeclared variable « " + Name + " »");
		}
		return save;
	}
	
	public void addInt(String Name) throws IOException
	{
		String addVariable[] = {Name, "int", "" + byteCounter};
		allVariables.add(addVariable);
		byteCounter += 4;
	}
	
	public void addBlock() throws IOException
	{
		String addVariable[] = {"", "block", "" + byteCounter};
		allVariables.add(addVariable);
		//byteCounter += 1;
	}
	
	public String removeBlock(String save) throws IOException
	{
		//int stack = 0;
		
		int i = (allVariables.size() - 1);
		while(!allVariables.elementAt(i)[1].equals("block"))
		{
			save = WrapVariables(save, allVariables.elementAt(i)[0], Integer.parseInt(allVariables.elementAt(i)[2]));
			
			allVariables.remove(i);
			i--;
		}
		//stack = byteCounter - Integer.parseInt(allVariables.elementAt(i)[2]);
		byteCounter = Integer.parseInt(allVariables.elementAt(i)[2]);
		allVariables.remove(i);
		
		//The next line restores the stack to the way it was before the block started.
		//save = EmitLn(save, "add esp, " + stack);
		
		return save;
	}
	
	public String BooleanFactor(String save)
	{
		return save;
	}
	
	public String BooleanTerm(String save, String buffer, int OperatorPosition, boolean Not) throws IOException
	{
		String more = GetSymbol();
		
		if(more.equals("&&"))
		{
			if(OperatorPosition != -1)
			{
				if(lastOperator.equals("||"))
				{
					save = EmitLn(save, "jmp " + PeekLabel(1));
					save = EmitFree(save, NewLabel() + ":");
				}
				save += buffer;
				if(Not)
				{
					save = EmitLn(save, conditions[2][OperatorPosition] + " " + PeekLabel(1));
				}
				else
				{
					save = EmitLn(save, conditions[1][OperatorPosition] + " " + PeekLabel(1));
				}
				if(lastOperator.equals("||"))
				{
					save = EmitFree(save, NewLabel() + ":");
				}
			}
			else
			{
				//save = EmitFree(save, NewLabel() + ":");
			}
			lastOperator = more;
		}
		else if(more.equals("||"))
		{
			if(OperatorPosition != -1)
			{
				save += buffer;
				if(Not)
				{
					save = EmitLn(save, conditions[1][OperatorPosition] + " " + PeekLabel(0));
				}
				else
				{
					save = EmitLn(save, conditions[2][OperatorPosition] + " " + PeekLabel(0));
				}
			}
			else
			{
				save = EmitFree(save, NewLabel() + ":");
				save = EmitLn(save, "jmp " + PeekLabel(1));
				save = EmitFree(save, NewLabel() + ":");
			}
			lastOperator = more;
		}
		else if(more.equals(""))
		{
			if(OperatorPosition != -1)
			{
				save += buffer;
				if(Not)
				{
					save = EmitLn(save, conditions[2][OperatorPosition] + " " + PeekLabel(1));
				}
				else
				{
					save = EmitLn(save, conditions[1][OperatorPosition] + " " + PeekLabel(1));
				}
	
				lastOperator = "";
			}
		}
		else
		{
			System.out.println("First one!");
			Abort("Systax error on tokens, delete these tokens « " + more + " »");
		}
		return save;
	}
	
	public String BooleanExpression(String save, boolean Not) throws IOException
	{
		do
		{
			if(Look == '!')
			{
				Match('!');
				
				if(!(IsAlpha(Look) || Look == '(')) //FIXME: This should check for a valid boolean variable, not an alpha
				{
					Expected("boolean");
				}
				//After this, a letter of a ( has to be matched.
				
				Not = true;
			}
			if(Look == '(')
			{
				Match('(');
				save = BooleanExpression(save, Not);
				Match(')');
				if(Look != ')')
				{
					lastOperator = "" + Look;
				}
				save = BooleanTerm(save, "", -1, Not);
				
				if(Not)
				{
					Not = false;
				}
				else
				{
					Not = true;
				}
			}
			else
			{
				//TODO: Add boolean variable.
				int OperatorPosition;
				String buffer = "";
				buffer = Expression(buffer);
				buffer = EmitLn(buffer, "push eax");
				
				OperatorPosition = GetOperator();
				buffer = Expression(buffer);
				
				buffer = EmitLn(buffer, "pop edx");
				buffer = EmitLn(buffer, "cmp eax, edx");
				
				save = BooleanTerm(save, buffer, OperatorPosition, Not);
			}
		}while(!lastOperator.equals(""));
		
		return save;
	}
	
	public String Condition(String save) throws IOException
	{
		Match('(');
		save = BooleanExpression(save, false);
		if(lastOperator.equals("||"))
		{
			save = EmitLn(save, "jmp " + PeekLabel(1));
		}
		Match(')');
		return save;
	}
	
	public String ifStatement(String save, String Name) throws IOException
	{
		Vector<String> possibilities = new Vector<String>();
		save = Condition(save);
		
		save = EmitFree(save, NewLabel() + ":");
		save = Block(save);
		
		boolean replace = false;
		
		//else if or else
		if(IsAlNum(Look)) //This is to make sure this is no '}'
		{
			Name = GetName();
			replace = true;
		}
		do
		{
			//I don't like placeHolder but it seems like it messes things up when it's
			//not there... I'll have to figure out what I was trying to do...
			String placeHolder = EmitFree("", NewLabel() + ":");

			if(Name.equals("else")) //This else if and else should be in here too.
			{
				Name = "";
				String buffer = "";
				if(IsAlNum(Look)) //This is to make sure this is no '{'
				{
					Name = GetName();
				}
				if(Name.equals("if")) //The else if and else should be in here too.
				{
					Name = "";
					buffer = Condition(buffer);
					buffer = EmitFree(buffer, NewLabel() + ":");
					buffer = Block(buffer);
				}
				else
				{
					buffer = Block(buffer);
				}
				placeHolder += buffer;
				possibilities.add(placeHolder);
			}
			else
			{
				if(replace)
				{
					ReplaceString(Name + Look);
					
					System.out.println("Replaced String " + Line + " " + Name + " " + Look);
				}
			}
			if(Look == 'e') //FIXME I guess that this can cause errors if there is a variable that starts with 'e'
			{
				Name = GetName();
			}
			if(!Name.equals("else"))
			{
				if(possibilities.size() == 0)
				{ //This has to be done because a label would have been lost.
					save += placeHolder;
				}
			}
		}while(Name.equals("else"));
		
		for(int i = 0; i < possibilities.size(); i++)
		{
			save = EmitLn(save, "jmp " + PeekLabel(0));
			save += possibilities.elementAt(i);
		}
		if(possibilities.size() != 0) //Blame placeHolder for this...
		{ //This is the label that has to be jumped to in order to skip to the end.
			save = EmitFree(save, NewLabel() + ":");
		}
		
		return save;
	}
	
	public String whileStatement(String save, String Name) throws IOException
	{
		Vector<String> possibilities = new Vector<String>();
		String backJump = PeekLabel(0);
		save = EmitLn(save, "push 0"); //FIXME: I believe this is not needed if there are no else while.
		save = EmitFree(save, NewLabel() + ":");
		save = Condition(save);
		
		save = EmitFree(save, NewLabel() + ":");
		save = Block(save);
		//Push 0 in the stack.
		save = EmitLn(save, "pop eax");
		save = EmitLn(save, "mov eax, 1"); //This tells the "else while" they are not needed.
		save = EmitLn(save, "push eax");
		save = EmitLn(save, "jmp " + backJump);
		
		boolean replace = false;
		
		//else if or else
		if(IsAlNum(Look)) //This is to make sure this is no '}'
		{
			Name = GetName();
			replace = true;
		}
		
		do
		{
			if(Name.equals("else")) //This else if and else should be in here too.
			{
				Name = "";
				String check = "";
				String buffer = "";
				if(IsAlNum(Look)) //This is to make sure this is no '{'
				{
					Name = GetName();
				}
				if(Name.equals("while")) //This else while and else should be in here too.
				{
					Name = "";
					
					check = EmitFree(check, NewLabel() + ":");
					check = EmitLn(check, "pop eax");
					check = EmitLn(check, "cmp eax, 1");
					buffer = EmitFree("", NewLabel() + ":");
					
					backJump = PeekLabel(-1);
					//buffer = EmitFree(buffer, NewLabel() + ":");
					buffer = Condition(buffer);
					
					buffer = EmitFree(buffer, NewLabel() + ":");
					buffer = Block(buffer);
					buffer = EmitLn(buffer, "pop eax");
					buffer = EmitLn(buffer, "mov eax, 1");
					buffer = EmitLn(buffer, "push eax");
					buffer = EmitLn(buffer, "jmp " + backJump);
				}
				else
				{ //In a while, there cannot be any "else" without condition or it would run forever.
					Expected("else while");
				}
				possibilities.add(check);
				possibilities.add(buffer);
			}
			else
			{
				if(replace)
				{
					ReplaceString(Name + Look);
					
					System.out.println("Replaced String " + Line + " " + Name + " " + Look);
				}
			}
			if(Look == 'e') //FIXME I guess that this can cause errors if there is a variable that starts with 'e'
			{				//Since my while exits only if Name != else, at this point, I check if there is an else in order to restart my loop.
							//It would be better to peak at the whole name.
				Name = GetName();
			}
		}while(Name.equals("else"));
		
		for(int i = 0; i < possibilities.size(); i += 2)
		{
			save += possibilities.elementAt(i);
			save = EmitLn(save, "je " + PeekLabel(0));
			save += possibilities.elementAt(i + 1);
		}
		//This is the label that has to be jumped to in order to skip to the end.
		save = EmitFree(save, NewLabel() + ":");
		save = EmitLn(save, "pop eax"); //Clears the stack.
		return save;
	}
	
	public String doWhileStatement(String save, String Name) throws IOException
	{
		String backJump = PeekLabel(0);
		save = EmitFree(save, NewLabel() + ":");
		
		save = Block(save);

		Name = GetName();
		if(Name.equals("while"))
		{
			save = Condition(save);
			save = EmitFree(save, NewLabel() + ":");
			save = EmitLn(save, "jmp " + backJump);
			save = EmitFree(save, NewLabel() + ":");
			if(Look == ';') //This way, the ';' is not forced on the programmer since
			{				//it's not harder to parse.
				Match(';');
			}
		}
		else
		{
			Expected("while");
		}
		return save;
	}
	
	public String loopStatement(String save, String Name) throws IOException
	{
		String backJump = PeekLabel(0);
		save = EmitFree(save, NewLabel() + ":");
		save = Block(save);
		save = EmitLn(save, "jmp " + backJump);
		
		return save;
	}
	
	public String intStatement(String save, String Name) throws IOException
	{
		Name = GetName();
		addInt(Name);
		
		if(IsArith())
		{
			save = Assignment(save, Name);
		}
		else if(Look == ';')
		{
			Match(';');
			//This initialises all variables to 0.
			ReplaceString("= 0;" + Look);
			save = Assignment(save, Name);
		}
		
		save = EmitLn(save, "sub esp, 4");
		
		if(Look == ',')
		{
			Match(',');
			save = intStatement(save, Name);
		}
		else
		{
			Match(';');
		}
		
		return save;
	}
	
	public String blockNesting(String save) throws IOException
	{
		addBlock();
		
		//save = EmitLn(save, "sub esp, 1");
		
		return save;
	}
	
	public String blockClear(String save) throws IOException
	{
		save = removeBlock(save);
		
		return save;
	}
	
	public String Block(String save) throws IOException
	{
		if(Look == '{') //With this, the starting '{' is now optional. Hopefully, it won't cause errors.
		{
			Match('{');
		}
		
		save = blockNesting(save);
		
		while(Look != '}') //TODO I think this will change later.
		{
			if(IsAlNum(Look))
			{
				String Name = GetName();
				if(Name.equals("if")) //This else if and else should be in here too.
				{
					save = ifStatement(save, Name);
				}
				if(Name.equals("while"))
				{
					System.out.println("While detected!");
					save = whileStatement(save, Name);
				}
				if(Name.equals("do"))
				{
					save = doWhileStatement(save, Name);
				}
				if(Name.equals("loop"))
				{
					save = loopStatement(save, Name);
				}
				if(Name.equals("int"))
				{
					save = intStatement(save, Name);
				}
				if(IsArith())
				{ //TODO: Make it work for "+=", "-=", "*=", "/=", "%=", "=+", "=-", "=*", "=/", "=%"
					save = Assignment(save, Name);
					Match(';');
				}
			}
			else
			{
				System.out.println("Second one! " + Look);
				String Symbol = GetSymbol();
				Abort("Systax error on tokens, delete these tokens « " + Symbol + " »");
			}
		}
		Match('}'); //TODO: Perhaps you'll want that handled by the caller!
		
		save = blockClear(save);
		
		return save;
	}
	
	public static void main(String args[]) throws IOException
	{
		VCompiler vCompiler = new VCompiler();
		vCompiler.code = vCompiler.Block(vCompiler.code);
		vCompiler.WrapFile();
		vCompiler.out.close();
		
		Process process = new ProcessBuilder("fasm", "Output.asm").start();
		InputStream is = process.getInputStream();
		InputStreamReader isr = new InputStreamReader(is);
		BufferedReader br = new BufferedReader(isr);
		String line = "";
		
		InputStream iserr = process.getErrorStream();
		InputStreamReader isrerr = new InputStreamReader(iserr);
		BufferedReader brerr = new BufferedReader(isrerr);
		String error = "";

		while ((line = br.readLine()) != null)
		{
			System.out.println(line);
		}
		while((error = brerr.readLine()) != null)
		{
			System.out.println(error);
		}
		
		System.out.println("\nRunning program!\n");
		
		Process process2 = new ProcessBuilder("Output.exe").start();
		InputStream is2 = process2.getInputStream();
		InputStreamReader isr2 = new InputStreamReader(is2);
		BufferedReader br2 = new BufferedReader(isr2);
		String line2;

		while ((line2 = br2.readLine()) != null)
		{
			System.out.println(line2);
		}
	}
}
